#pragma once
#include <elasticlient/client.h>
#include <cpr/cpr.h>
#include <json/json.h>
#include <iostream>
#include <memory>
#include "logger.hpp"

namespace im
{
// Json序列化
bool Serialize(const Json::Value& value, std::string& str)
{
    // writer、reader都不能直接实例化对象，需要工厂实例化对象
    Json::StreamWriterBuilder swb;
    // 用智能指针管理返回的指针对象
    std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
    // 序列化
    std::stringstream ss;
    int ret = sw->write(value, &ss);
    if (ret != 0) {
        std::cout << "Json序列化失败\n";
        return false;
    }
    str = ss.str();
    return true;
}

// Json反序列化
bool UnSerialize(Json::Value& value, std::string& str)
{
    // writer、reader都不能直接实例化对象，需要工厂实例化对象
    Json::CharReaderBuilder crb;
    // 用智能指针管理返回的指针对象
    std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
    // 反序列化
    std::string err;
    int ret = cr->parse(str.c_str(), str.c_str() + str.size(), &value, &err);
    if (ret == false) {
        std::cout << "Json反序列化失败\n" << err << std::endl;
        return false;
    }
    return true;
}

// 创建索引
class ESIndex
{
private:
    std::shared_ptr<elasticlient::Client> _client; // ES客户端
    std::string _name;  // 索引名
    std::string _type;  // 索引类型
    Json::Value _index; // 最终组织Json::Value对象
    Json::Value _properties;
public:
    ESIndex(std::shared_ptr<elasticlient::Client>& client, const std::string& name, std::string type = "_doc")
        : _client(client)
        , _name(name)
        , _type(type)
    {
        // 将settings格式先组织到_index中
        Json::Value tokenizer, ik, analyzer, analysis, settings;
        ik["tokenizer"] =  "ik_max_word";
        analyzer["ik"] = ik;
        analysis["analyzer"] = analyzer;
        settings["analysis"] = analysis;
        _index["settings"] = settings;
    }
    ESIndex& append(const std::string& key, const std::string& type = "text", 
                    const std::string& analyzer = "ik_max_word", bool enable = true)
    {
        // 组织properties
        Json::Value fields;
        fields["type"] = type;
        fields["analyzer"] = analyzer;

        if (enable == false) fields["enable"] = false;
        _properties[key] = fields;
        return *this;
    }
    // 创建索引需要索引名、索引类型、索引id、请求正文
    bool create(const std::string& index_id = "default_index_id")
    {
        // 组织最终的 _index
        Json::Value mappings;
        mappings["dynamic"] = true;
        mappings["properties"] = _properties;
        _index["mappings"] = mappings;

        std::string body;
        bool ret = Serialize(_index, body);
        if (ret == false) {
            LOG_ERROR("创建索引时序列化失败!");
            return false;
        }

        // LOG_DEBUG("{}", body);


        try {
            auto rsp = _client->index(_name, _type, index_id, body);
            if (rsp.status_code < 200 || rsp.status_code >= 300) {
                LOG_ERROR("创建索引 {} 失败: {}", _name, rsp.status_code);
                return false;
            } 
        } catch (std::exception& e) {
            LOG_ERROR("创建索引 {} 失败: {}", _name, e.what());
            return false;
        }
        return true;
    }
};
// 新增数据
class ESInsert
{
private:
    std::shared_ptr<elasticlient::Client> _client; // ES客户端
    std::string _name;  // 索引名
    std::string _type;  // 索引类型
    Json::Value _item;  // 新增数据的Json格式
public:
    ESInsert(std::shared_ptr<elasticlient::Client>& client, const std::string& name, 
            const std::string& type = "_doc")
        : _client(client)
        , _name(name)
        , _type(type)
    {}
    template<typename T>
    ESInsert& append(const std::string& key, const T& val)
    {
        _item[key] = val;
        return *this;
    }
    bool insert(const std::string& id = "")
    {
        std::string body;
        bool ret = Serialize(_item, body);
        if (ret == false) {
            LOG_ERROR("索引序列化失败!");
            return false;
        }

        LOG_DEBUG("{}", body);

        try {
            auto rsp = _client->index(_name, _type, id, body);
            if (rsp.status_code < 200 || rsp.status_code >= 300) {
                LOG_ERROR("新增数据 {} 失败: {}", body, rsp.status_code);
                return false;
            } 
        } catch (std::exception& e) {
            LOG_ERROR("新增数据 {} 失败: {}", body, e.what());
            return false;
        }
        return true;
    }
};
// 删除数据
class ESRemove
{
private:
    std::shared_ptr<elasticlient::Client> _client; // ES客户端
    std::string _name;  // 索引名
    std::string _type;  // 索引类型
public:
    ESRemove(std::shared_ptr<elasticlient::Client>& client, const std::string& name, 
            const std::string& type = "_doc")
        : _client(client)
        , _name(name)
        , _type(type)
    {}
    bool remove(const std::string& id)
    {
        try {
            auto rsp = _client->remove(_name, _type, id);
            if (rsp.status_code < 200 || rsp.status_code >= 300) {
                LOG_ERROR("删除数据 {} 失败: {}", id, rsp.status_code);
                return false;
            } 
        } catch (std::exception& e) {
            LOG_ERROR("删除数据 {} 失败: {}", id, e.what());
            return false;
        }
        return true;
    }
};
// 检索数据
class ESSearch
{
private:
    std::shared_ptr<elasticlient::Client> _client; // ES客户端
    std::string _name;  // 索引名
    std::string _type;  // 索引类型
    Json::Value _must_not; // 需要过滤的key
    Json::Value _should;   // 要匹配的key，是一种逻辑或
    Json::Value _must;     // 必须要匹配的key，是一种逻辑与
public:
    ESSearch(std::shared_ptr<elasticlient::Client>& client, const std::string& name, 
        const std::string& type = "_doc")
        : _client(client)
        , _name(name)
        , _type(type)
    {}
    // 添加过滤条件
    ESSearch& append_must_not(const std::string& key, const std::vector<std::string>& vals)
    {
        // 组织为标准Json格式到_must_not中
        Json::Value fields;
        for (const auto& val : vals) fields[key].append(val);
        Json::Value terms;
        terms["terms"] = fields;

        _must_not.append(terms);

        return *this;
    }
    // 添加必要的匹配条件
    ESSearch& append_should(const std::string& key, const std::string& val)
    {
        Json::Value field;
        field[key] = val;
        Json::Value match;
        match["match"] = field;

        _should.append(match);

        return *this;
    }
    // 
    ESSearch& append_must_term(const std::string &key, const std::string &val) {
        Json::Value field;
        field[key] = val;
        Json::Value term;
        term["term"] = field;
        _must.append(term);
        return *this;
    }
    // 必须完全匹配
    ESSearch& append_must_match(const std::string &key, const std::string &val){
        Json::Value field;
        field[key] = val;
        Json::Value match;
        match["match"] = field;
        _must.append(match);
        return *this;
    }
    Json::Value search()
    {
        // 1. 构建标准的搜索Json串，并序列化
        Json::Value cond;
        if (_must_not.empty() == false) cond["must_not"] = _must_not;
        if (_should.empty() == false) cond["should"] = _should;
        if (_must.empty() == false) cond["must"] = _must;
        
        Json::Value query;
        query["bool"] = cond;

        Json::Value root;
        root["query"] = query;
    
        std::string body;
        bool ret = Serialize(root, body);
        if (ret == false) {
            LOG_ERROR("索引序列化失败!");
            return false;
        }

        LOG_DEBUG("{}", body);


        // 2. 发起搜索请求
        cpr::Response rsp;
        try {
            rsp = _client->search(_name, _type, body);
            if (rsp.status_code < 200 || rsp.status_code >= 300) {
                LOG_ERROR("检索数据 {} 失败: {}", body, rsp.status_code);
                return Json::Value();
            }
        } catch (std::exception& e) {
            LOG_ERROR("检索数据 {} 失败: {}", body, e.what());
            return Json::Value();
        }
        // 3. 对rsp响应字符串反序列化
        Json::Value rsp_json;
        ret = UnSerialize(rsp_json, rsp.text);
        if (ret == false) {
            LOG_ERROR("检索数据 {} 反序列化失败!", rsp.text);
            return Json::Value();
        }

        // 4. 返回hits中的hits数组
        return rsp_json["hits"]["hits"];
    }
};
}